module i2c_tran(
  input             clk_50M,
  input             rst_n,

  input             data_valid,
  input             mode, // 0: write, 1: read
  input       [7:0] devaddr,
  input       [7:0] subaddr1,
  input       [7:0] subaddr2,
  input       [7:0] data_in,

  output  reg       tran_done,
  output            ack_r,
  output  reg [7:0] data_out,

  // i2c port
  output        scl,
  inout         sda
);

//--------------------i2c_sclk freq-------------------------
parameter           I2C_FREQ   =    500;
parameter           TRANSFER   =    1;
parameter           CAPTURE    =    250;

//i2c_sclk
reg     [10:0]       sclk_cnt;
always @(posedge clk_50M) begin
    if(rst_n == 1'b0)
        sclk_cnt <= 'd1;
    else if(sclk_cnt == I2C_FREQ - 1'b1)
        sclk_cnt <= 'd0;
    else
        sclk_cnt <= sclk_cnt + 1'b1;
end

reg     i2c_sclk_r;
always @(posedge clk_50M) begin
    if(rst_n == 1'b0)
        i2c_sclk_r <= 1'b0;
    else if(sclk_cnt >= (I2C_FREQ>>2)*1 && sclk_cnt <= (I2C_FREQ>>2)*3)
        i2c_sclk_r <= 1'b1;
    else
        i2c_sclk_r <= 1'b0;
end

wire    transfer_en, capture_en;
assign  transfer_en = (sclk_cnt == TRANSFER - 1)? 1'b1: 1'b0;
assign  capture_en  = (sclk_cnt == CAPTURE - 1)? 1'b1: 1'b0;
reg transfer_en_d1;
always @(posedge clk_50M)
transfer_en_d1 <= transfer_en;
// -----------------------------------------------------------------

// connect i2c driver module
reg i2c_valid;
reg [1:0] i2c_ctrl;  // 00: start 01: stop 10: write 11: read
reg [7:0] i2c_d_in;
wire [7:0] i2c_d_out;
wire i2c_done; 
i2c_driver u1 (
  .rst_n(rst_n), .clk_50M(clk_50M), 

  .i2c_sclk(i2c_sclk_r), 
  .transfer_en(transfer_en), 
  .capture_en(capture_en),

  .i2c_ctrl(i2c_ctrl), 
  .i2c_d_in(i2c_d_in), 
  .i2c_d_out(i2c_d_out), 

  .i2c_valid(i2c_valid),
  .i2c_done(i2c_done), 

  .scl(scl), .sda(sda),
  .ack_r(ack_r)
  );

// ---------------------detect valid posedge-----------------------
reg valid_d1, valid_d2, valid_d3;
always @(posedge clk_50M) begin
if (!rst_n) begin
  valid_d1 <= 0;
  valid_d2 <= 0;
  valid_d3 <= 0;
end else begin
  valid_d1 <= data_valid;
  valid_d2 <= valid_d1;
  valid_d3 <= valid_d2;
end
end

wire valid_pe;
assign valid_pe = (data_valid && (~valid_d1)); // delay the valid
// ----------------------------------------------------------------

// --------------------- get addr and data ------------------------
reg [7:0] devaddr_r,subaddr1_r,subaddr2_r,data_in_r;
reg mode_r;
always @(posedge clk_50M) begin
  if (!rst_n) begin
    devaddr_r <= 0;
    subaddr1_r <= 0;
    subaddr2_r <= 0;
    data_in_r <= 0;
    mode_r <= 0;
  end else begin
    if (valid_pe) begin // capture addr and data at the pe of valid
      devaddr_r <= devaddr;
      subaddr1_r <= subaddr1;
      subaddr2_r <= subaddr2;
      data_in_r <= data_in;
      mode_r <= mode;
    end else begin
      devaddr_r <= devaddr_r;
      subaddr1_r <= subaddr1_r;
      subaddr2_r <= subaddr2_r;
      data_in_r <= data_in_r;
      mode_r <= mode_r;
  end
  end
end
// ---------------------------------------------------------------

// ---------------------state machine------------------------------
reg [3:0]  state, next_state;
parameter idle     = 4'd0;
parameter start    = 4'd1;
parameter wr_dev   = 4'd2;
parameter wr_sub1  = 4'd3;
parameter wr_sub2  = 4'd4;
parameter wr_data  = 4'd5;
parameter rd_start = 4'd6;
parameter rd_dev   = 4'd7;
parameter rd_data  = 4'd8;
parameter stop     = 4'd9;

always @(posedge clk_50M) begin
  if (!rst_n) 
    state <= idle; 
  else 
    state <= next_state;
end

always @ (*) begin
  case (state)
  idle:     if ((valid_d3) && (i2c_done) && (transfer_en)) // use valid pe as the first start signal
              next_state = start; 
            else 
              next_state = idle;

  start:    if ((i2c_done) && (transfer_en_d1))
              next_state = wr_dev; 
            else 
              next_state = start;

  wr_dev:   if ((i2c_done) && (transfer_en_d1))
              next_state = wr_sub1; 
            else 
              next_state = wr_dev;

  wr_sub1:  if ((i2c_done) && (transfer_en_d1))
              next_state = wr_sub2; 
            else 
              next_state = wr_sub1;

  wr_sub2:  if ((i2c_done) && (transfer_en_d1)) begin 
              if (!mode_r) 
                next_state = wr_data; 
              else 
                next_state = rd_start; 
            end else next_state = wr_sub2;
           
  wr_data:  if ((i2c_done) && (transfer_en_d1))
              next_state = stop; 
            else 
              next_state = wr_data;

  rd_start: if ((i2c_done) && (transfer_en_d1))
              next_state = rd_dev; 
            else 
              next_state = rd_start;

  rd_dev:   if ((i2c_done) && (transfer_en_d1)) 
              next_state = rd_data; 
            else 
              next_state = rd_dev;

  rd_data:  if ((i2c_done) && (transfer_en_d1))
              next_state = stop; 
            else 
              next_state = rd_data;

  stop:     if ((i2c_done) && (transfer_en_d1))
              next_state = idle; 
            else 
              next_state = stop;

  default:  next_state = idle;
  endcase
end

always @(*) begin
  case (state)
  start:   begin i2c_ctrl <= 2'b00; i2c_d_in <= 8'h00; end
  wr_dev:  begin i2c_ctrl <= 2'b10; i2c_d_in <= {devaddr_r[7:1],1'b0}; end
  wr_sub1: begin i2c_ctrl <= 2'b10; i2c_d_in <= subaddr1_r; end
  wr_sub2: begin i2c_ctrl <= 2'b10; i2c_d_in <= subaddr2_r; end
  wr_data: begin i2c_ctrl <= 2'b10; i2c_d_in <= data_in_r; end

  rd_start:begin i2c_ctrl <= 2'b00; i2c_d_in <= 8'h00; end
  rd_dev:  begin i2c_ctrl <= 2'b10; i2c_d_in <= {devaddr_r[7:1],mode_r}; end
  rd_data: begin i2c_ctrl <= 2'b11; i2c_d_in <= 8'h00; end

  stop:    begin i2c_ctrl <= 2'b01; i2c_d_in <= 8'h00; end
  default: begin i2c_ctrl <= 2'b00; i2c_d_in <= 8'h00; end
  endcase
end
// ----------------------------------------------------------------

// -------------------------i2c valid signal-----------------------
always @(posedge clk_50M) begin
  if (!rst_n)
    i2c_valid <= 0;
  else if (state == idle) 
    i2c_valid <= valid_d2 && transfer_en;
  else i2c_valid <= transfer_en && i2c_done;
end
// ----------------------------------------------------------------

// ------------------tran done------------------------------------
always @(posedge clk_50M) begin
  if (!rst_n) 
    tran_done <= 1;
  else if ((state == idle)&&(!data_valid)) 
    tran_done <= 1;
  else 
    tran_done <= 0;
end
// ---------------------------------------------------------------

// ----------data out(valid in read mode)-------------------------
always @(posedge clk_50M) begin
  if (!rst_n)
    data_out <= 8'bx;
  else if ((state == stop) && (capture_en) && (mode_r == 1'b1) && (i2c_done))
    data_out <= i2c_d_out;
  else if (valid_pe)
    data_out <= 8'bx;
  else data_out <= data_out;
end
// ----------------------------------------------------------------

endmodule